package com.stars.easyms.datasource.enums;

import com.stars.easyms.base.util.ClassUtil;
import com.stars.easyms.datasource.common.EasyMsDataSourceConstant;
import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.NonNull;

import java.util.*;

/**
 * 数据库类型
 *
 * @author guoguifang
 * @date 2018-10-19 10:07
 * @since 1.0.0
 */
public enum DatabaseType {

    /**
     * ORACLE
     */
    ORACLE("oracle", "oracle.jdbc.OracleDriver", "jdbc:oracle:thin:@",
            EasyMsDataSourceConstant.CONNECT_URL_COLON_SIGN, null, "SELECT 1 FROM DUAL"),

    /**
     * MYSQL8.x
     */
    MYSQL_8("mysql", "com.mysql.cj.jdbc.Driver", "jdbc:mysql://", EasyMsDataSourceConstant.CONNECT_URL_FORWARD_SLASH_SIGN,
            "useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true&zeroDateTimeBehavior=CONVERT_TO_NULL&rewriteBatchedStatements=true&useSSL=false&allowPublicKeyRetrieval=true&useTimezone=true&serverTimezone=Asia/Shanghai", "SELECT 1"),

    /**
     * MYSQL5.x
     */
    MYSQL_5("mysql", "com.mysql.jdbc.Driver", "jdbc:mysql://", EasyMsDataSourceConstant.CONNECT_URL_FORWARD_SLASH_SIGN,
            "useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&rewriteBatchedStatements=true&useSSL=false&useTimezone=true&serverTimezone=Asia/Shanghai", "SELECT 1"),

    /**
     * SQLSERVER 2005
     */
    SQLSERVER_2005("sqlserver", "com.microsoft.sqlserver.jdbc.SQLServerDriver", "jdbc:sqlserver://",
            ";DatabaseName=", null, "select 1"),

    /**
     * SQLSERVER 2000
     */
    SQLSERVER_2000("sqlserver", "com.microsoft.jdbc.sqlserver.SQLServerDriver", "jdbc:microsoft:sqlserver://",
            ";DatabaseName=", null, "select 1"),

    /**
     * DB2
     */
    DB2("db2", "com.ibm.db2.jcc.DB2Driver", "jdbc:db2://",
            EasyMsDataSourceConstant.CONNECT_URL_FORWARD_SLASH_SIGN, null, "select 1 from sysibm.sysdummy1"),

    /**
     * PostgreSQL
     */
    POSTGRESQL("postgresql", "org.postgresql.Driver", "jdbc:postgresql://",
            EasyMsDataSourceConstant.CONNECT_URL_FORWARD_SLASH_SIGN, null, "select version()");

    private String dbType;

    private String driverClassName;

    private String connectPrefix;

    private String connectSuffix;

    private String defaultConnectParams;

    private Map<String, String> defaultConnectParamsMap;

    private String defaultValidationQuery;

    private static Map<String, List<DatabaseType>> dbTypeLookup;

    public static DatabaseType forDbType(String dbType) {
        if (StringUtils.isBlank(dbType)) {
            return null;
        }
        List<DatabaseType> databaseTypeList = dbTypeLookup.get(dbType);
        if (databaseTypeList != null) {
            if (databaseTypeList.size() == 1) {
                return databaseTypeList.get(0);
            }
            for (DatabaseType databaseType : databaseTypeList) {
                if (ClassUtil.isExist(databaseType.driverClassName)) {
                    return databaseType;
                }
            }
        }
        return null;
    }

    public static DatabaseType forUrlOrDriverClassName(String value) {
        if (StringUtils.isNotBlank(value)) {
            if (value.contains(DatabaseType.MYSQL_8.dbType)) {
                return ClassUtil.isExist(DatabaseType.MYSQL_8.driverClassName) ? DatabaseType.MYSQL_8 : DatabaseType.MYSQL_5;
            }
            if (value.startsWith(SQLSERVER_2005.connectPrefix) || value.equals(SQLSERVER_2005.driverClassName)) {
                return SQLSERVER_2005;
            }
            if (value.startsWith(SQLSERVER_2000.connectPrefix) || value.equals(SQLSERVER_2000.driverClassName)) {
                return SQLSERVER_2000;
            }
            for (DatabaseType databaseType : values()) {
                if (value.contains(databaseType.dbType)) {
                    return databaseType;
                }
            }
        }
        return null;
    }

    DatabaseType(String dbType, String driverClassName, String connectPrefix, String connectSuffix, String defaultConnectParams, String defaultValidationQuery) {
        this.dbType = dbType;
        this.driverClassName = driverClassName;
        this.connectPrefix = connectPrefix;
        this.connectSuffix = connectSuffix;
        this.defaultValidationQuery = defaultValidationQuery;
        if (defaultConnectParams != null) {
            Map<String, String> connectParamsMap = new LinkedHashMap<>();
            String[] connectParamsItems = defaultConnectParams.split(EasyMsDataSourceConstant.CONNECT_PARAMS_JOINT_SIGN);
            for (String connectParamsItem : connectParamsItems) {
                String[] entry = connectParamsItem.split(EasyMsDataSourceConstant.CONNECT_PARAMS_EQUAL_SIGN);
                connectParamsMap.put(entry[0], entry[1]);
            }
            this.defaultConnectParams = defaultConnectParams;
            this.defaultConnectParamsMap = Collections.unmodifiableMap(connectParamsMap);
        } else {
            this.defaultConnectParams = "";
            this.defaultConnectParamsMap = Collections.emptyMap();
        }
    }

    static {
        dbTypeLookup = new HashMap<>();
        for (DatabaseType databaseType : values()) {
            dbTypeLookup.computeIfAbsent(databaseType.dbType, key -> new ArrayList<>()).add(databaseType);
        }
    }

    public String getDbType() {
        return dbType;
    }

    public String getDriverClassName() {
        return driverClassName;
    }

    public String getConnectPrefix() {
        return connectPrefix;
    }

    public String getConnectSuffix() {
        return connectSuffix;
    }

    @NonNull
    public String getDefaultConnectParams() {
        return defaultConnectParams;
    }

    @NonNull
    public Map<String, String> getDefaultConnectParamsMap() {
        return defaultConnectParamsMap;
    }

    public String getDefaultValidationQuery() {
        return defaultValidationQuery;
    }
}
